/**
  ******************************************************************************
  * @file 
  * @brief 
  * @author lixin  
	          2019/01/28 zsy modified
  * @version V1.0.0
  * @date 2019/01/28
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "w5500_fun.h"
#include "w5500.h"
#include "A_shell.h"
/* Private defines -----------------------------------------------------------*/
#define _W5500_SPI_VDM_OP_          0x00
#define _W5500_SPI_FDM_OP_LEN1_     0x01
#define _W5500_SPI_FDM_OP_LEN2_     0x02
#define _W5500_SPI_FDM_OP_LEN4_     0x03
/* Private Struct  -----------------------------------------------------------*/


/* Private enum    -----------------------------------------------------------*/


/* Private Variable  ---------------------------------------------------------*/
//Pointer

//Array
static uint16_t SSIZE[_WIZCHIP_SOCK_NUM_];    /**< Max Tx buffer size by each channel */
static uint16_t RSIZE[_WIZCHIP_SOCK_NUM_];    /**< Max Rx buffer size by each channel */

//Const

/* Private function prototypes -----------------------------------------------*/

/**
* @brief  Get socket xx rx buff size.
* @param  s      : Socket Number.
* @retval rx buff size.
*/
uint16_t getSn_RxMAX(uint8_t sn)
{
	return RSIZE[sn];
}

/**
* @brief  Get socket xx tx buff size.
* @param  s      : Socket Number.
* @retval tx buff size.
*/
uint16_t getSn_TxMAX(uint8_t sn)
{
	return SSIZE[sn];
}


///***********************************************************************************
// * @brief 
// * ex:
// * @par 
// * None
// * @par Called functions:
// * None
// * @retval void None
// **********************************************************************************/
//static void IINCHIP_CSoff(void)
//{
//	W5500_CSCmd(DISABLE); 	
//}

///***********************************************************************************
// * @brief 
// * ex:
// * @par 
// * None
// * @par Called functions:
// * None
// * @retval void None
// **********************************************************************************/
//static void IINCHIP_CSon(void)
//{
//	W5500_CSCmd(ENABLE); 
//}

/**
* @brief  SPI sends a byte of data.
* @param  dat      : Specify a byte of data to send.
* @retval Return invalid data.
*/
static void IINCHIP_SpiSendData(uint8_t dat)
{
	SPI_WriteByte(dat);
}

/**
* @brief  SPI receiv a byte of data.
* @param  dat      : Send a byte of invalid data.
* @retval Return a byte of read data.
*/
static uint8_t IINCHIP_SpiReadData(void)
{
	uint8_t value = 0;

	value = SPI_ReadByte();
	
	return value;	
}

/**
* @brief  Write a byte of data to the chip register.
* @param  dat      : Specify a data to write.
* @param  addrbsb  : Specify a register address to write data.
* @retval NONE
*/
void WIZCHIP_WRITE( uint32_t addrbsb, uint8_t data)
{
	uint8_t spi_data[4];
    WIZCHIP_CRITICAL_ENTER();                              // Interrupt Service Routine Disable
#if !defined (SPI_DMA)
    W5500_CSCmd(0);                                   // CS=0, SPI start
#endif
    addrbsb |= (_W5500_SPI_WRITE_ | _W5500_SPI_VDM_OP_);
#if !defined (SPI_DMA)
	IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16);   // Address byte 1
	IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8);   // Address byte 2
	IINCHIP_SpiSendData( (addrbsb & 0x000000FF)>> 0);   // Data write command and Write data length 1
	IINCHIP_SpiSendData(data);                          // Data write (write 1byte data)
#else
    
    spi_data[0] = (addrbsb & 0x00FF0000) >> 16;
    spi_data[1] = (addrbsb & 0x0000FF00) >> 8;
    spi_data[2] = (addrbsb & 0x000000FF) >> 0;
    SPI_DMA_WRITE(spi_data, &data, 1);    
#endif    
	
#if !defined (SPI_DMA)    
    W5500_CSCmd(1);                                 // CS=1,  SPI end
#endif   
	WIZCHIP_CRITICAL_EXIT();                               // Interrupt Service Routine Enable
}

/**
* @brief  Read a byte of data from the chip's registers.
* @param  addrbsb  : Specify a register address to read data.
* @retval Return a byte of data read.
*/
uint8_t WIZCHIP_READ(uint32_t addrbsb)
{
	uint8_t data = 0;
    uint8_t spi_data[3];
    
    WIZCHIP_CRITICAL_ENTER();                            // Interrupt Service Routine Disable

#if !defined(SPI_DMA)	
	W5500_CSCmd(0);                                 // CS=0, SPI start
#endif    

    
#if !defined(SPI_DMA)		
    
	IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16); // Address byte 1
	IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8); // Address byte 2
	IINCHIP_SpiSendData( (addrbsb & 0x000000FF)>> 0)    ; // Data read command and Read data length 1
	data = IINCHIP_SpiReadData();                     // Data read (read 1byte data)
#else
    addrbsb |= (_W5500_SPI_READ_ | _W5500_SPI_VDM_OP_);	
    spi_data[0] = (addrbsb & 0x00FF0000) >> 16;
    spi_data[1] = (addrbsb & 0x0000FF00) >> 8;
    spi_data[2] = (addrbsb & 0x000000FF) >> 0;
    SPI_DMA_READ(spi_data, &data, 1);    
#endif  

#if !defined(SPI_DMA)	    
	W5500_CSCmd(1);                               // CS=1,  SPI end
#endif      
	WIZCHIP_CRITICAL_EXIT();                             // Interrupt Service Routine Enable
	return data;    
}

/**
@brief  get socket TX free buf size

This gives free buffer size of transmit buffer. This is the data size that user can transmit.
User should check this value first and control the size of transmitting data
*/
uint16_t getSn_TX_FSR(SOCKET s)
{
  uint16_t val=0,val1=0;
  do
  {
    val1 = WIZCHIP_READ(Sn_TX_FSR0(s));
    val1 = (val1 << 8) + WIZCHIP_READ(Sn_TX_FSR1(s));
      if (val1 != 0)
    {
        val = WIZCHIP_READ(Sn_TX_FSR0(s));
        val = (val << 8) + WIZCHIP_READ(Sn_TX_FSR1(s));
    }
  } while (val != val1);
   return val;
}

/**
@brief   get socket RX recv buf size

This gives size of received data in receive buffer.
*/
uint16_t getSn_RX_RSR(SOCKET s)
{
  uint16_t val=0,val1=0;
  do
  {
    val1 = WIZCHIP_READ(Sn_RX_RSR0(s));
    val1 = (val1 << 8) + WIZCHIP_READ(Sn_RX_RSR1(s));
    if(val1 != 0)
    {
        val = WIZCHIP_READ(Sn_RX_RSR0(s));
        val = (val << 8) + WIZCHIP_READ(Sn_RX_RSR1(s));
    }
  } while (val != val1);
   return val;
}


/**
* @brief  Write several bytes of data to the specified register. 
* @param  addrbsb  : Specify a register address to write data.
* @param  buf      : Specify a data buffer to be written.
* @param  len      : Specify the length of the data to be written.
* @retval Return the length of the written data.
*/
uint16_t WIZCHIP_WRITE_BUF(uint32_t addrbsb,uint8_t* buf,uint16_t len)
{
	uint16_t idx = 0;
    uint8_t spi_data[3];
	if(len == 0) 
		Ashell_print("Unexpected2 length 0\r\n");

	WIZCHIP_CRITICAL_ENTER();
#if !defined (SPI_DMA)
	W5500_CSCmd(0);                               // CS=0, SPI start
#endif

#if !defined (SPI_DMA)
    addrbsb |= (_W5500_SPI_WRITE_ | _W5500_SPI_VDM_OP_);
	IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16);// Address byte 1
    IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8);// Address byte 2
	IINCHIP_SpiSendData( (addrbsb & 0x000000FF)>> 0);    // Data write command and Write data length 1
	for(idx = 0; idx < len; idx ++) 
	{       
		IINCHIP_SpiSendData(buf[idx]);
	}
#else
    addrbsb |= (_W5500_SPI_WRITE_ | _W5500_SPI_VDM_OP_);
    spi_data[0] = (addrbsb & 0x00FF0000) >> 16;
    spi_data[1] = (addrbsb & 0x0000FF00) >> 8;
    spi_data[2] = (addrbsb & 0x000000FF) >> 0;
    SPI_DMA_WRITE(spi_data, buf, len);        
#endif    
    
#if !defined (SPI_DMA)
	W5500_CSCmd(1);                              // CS=1, SPI end
#endif
    
    WIZCHIP_CRITICAL_EXIT();                         // Interrupt Service Routine Enable    
	return len;  
}

/**
* @brief  Send several bytes of data to be sent to the socket. 
* @param  sn   : Specify the socket number to use.
* @param  buf  : Specify a data buffer to be send.
* @param  len  : Specify the length of the data to be send.
* @retval NONE.
*/
void wiz_send_data(uint8_t sn, uint8_t *wizdata, uint16_t len)
{
	uint16_t ptr = 0;
	uint32_t addrsel = 0;
	if(len == 0)  return;
	ptr = getSn_TX_WR(sn);
	//M20140501 : implict type casting -> explict type casting
	//addrsel = (ptr << 8) + (WIZCHIP_TXBUF_BLOCK(sn) << 3);
	addrsel = ((uint32_t)ptr << 8) + (WIZCHIP_TXBUF_BLOCK(sn) << 3);
	//
	WIZCHIP_WRITE_BUF(addrsel,wizdata, len);

	ptr += len;
	setSn_TX_WR(sn,ptr);
}

/**
* @brief  Read several bytes of data to the specified register. 
* @param  addrbsb  : Specify a register address to read data.
* @param  buf      : Specify to save the read data buffer.
* @param  len      : Specify the length of the data to be read.
* @retval Return the length of the read data.
*/
uint16_t WIZCHIP_READ_BUF(uint32_t addrbsb, uint8_t* buf,uint16_t len)
{
	uint16_t idx = 0;
    uint8_t spi_data[3];
	if(len == 0)
	{
		//Ashell_print("Unexpected2 length 0\r\n");
	}

	WIZCHIP_CRITICAL_ENTER();
   
    #if !defined (SPI_DMA)
        W5500_CSCmd(0);                              // CS=0, SPI start
    #endif
    
#if !defined (SPI_DMA)
    addrbsb |= (_W5500_SPI_READ_ | _W5500_SPI_VDM_OP_);
    IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16); // Address byte 1
    IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8); // Address byte 2
    IINCHIP_SpiSendData( (addrbsb & 0x000000FF));     // Data write command and Write data length 1

    for(idx = 0 ; idx < len ; idx++)
    {
        buf[idx] = IINCHIP_SpiReadData();
    }
    
#else
        addrbsb |= (_W5500_SPI_READ_ | _W5500_SPI_VDM_OP_);
        spi_data[0] = (addrbsb & 0x00FF0000) >> 16;
        spi_data[1] = (addrbsb & 0x0000FF00) >> 8;
        spi_data[2] = (addrbsb & 0x000000FF) >> 0;
        SPI_DMA_READ(spi_data, buf, len);
#endif
    
    #if !defined (SPI_DMA)
        W5500_CSCmd(1);                             // CS=1, SPI end
    #endif
	                           
	
    WIZCHIP_CRITICAL_EXIT();                             // Interrupt Service Routine Enable

	return len;
}

/**
* @brief  Receive several bytes of data from the specified socket. 
* @param  sn   : Specify the socket number to use.
* @param  buf  : Specify to save the read data buffer.
* @param  len  : Specify the length of the data to be receive.
* @retval NONE.
*/
void wiz_recv_data(uint8_t sn, uint8_t *wizdata, uint16_t len)
{
	uint16_t ptr = 0;
	uint32_t addrsel = 0;

	if(len == 0) return;
	ptr = getSn_RX_RD(sn);
	
	addrsel = ((uint32_t)ptr << 8) + (WIZCHIP_RXBUF_BLOCK(sn) << 3);
	
	WIZCHIP_READ_BUF(addrsel, wizdata, len);
	
	ptr += len;

	setSn_RX_RD(sn,ptr);
}

/**
@brief   This function is being called by send() and sendto() function also.

This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer
register. User should read upper byte first and lower byte later to get proper value.
*/
void send_data_processing(SOCKET s, uint8_t *data, uint16_t len)
{
  uint16_t ptr = 0;
  uint32_t addrbsb = 0;
	
  if(len == 0)
  {
    Ashell_print("CH: %d Unexpected1 length 0\r\n", s);
    return;
  }
	ptr =	getSn_TX_WR(s);

  addrbsb = (uint32_t)(ptr<<8) + (s<<5) + 0x10;
  WIZCHIP_WRITE_BUF(addrbsb, data, len);
  
//  Ashell_print("wr addr:%x len:%d\r\n",ptr,len);
//  Ashell_print("send data:\r\n ");
//  for(int i = 0; i < len;i++)
//  {
//      Ashell_print("%x ", data[i]);
//  }
//  Ashell_print("\r\n");
  
  ptr += len;
  setSn_TX_WR(s, ptr)
}

/**
@brief  This function is being called by recv() also.

This function read the Rx read pointer register
and after copy the data from receive buffer update the Rx write pointer register.
User should read upper byte first and lower byte later to get proper value.
*/
void recv_data_processing(SOCKET s, uint8_t *data, uint16_t len)
{
  uint16_t ptr = 0;
  uint32_t addrbsb = 0;
  
  if(len == 0)
  {
    //Ashell_print("CH: %d Unexpected2 length 0\r\n", s);
    return;
  }
	ptr = getSn_RX_RD(s);
	
  addrbsb = (uint32_t)(ptr<<8) + (s<<5) + 0x18;
	
  WIZCHIP_READ_BUF(addrbsb, data, len);
	
  ptr += len;
	
	setSn_RX_RD(s,ptr);
}

/**
@brief  This function is for resetting of the iinchip. Initializes the iinchip to work in whether DIRECT or INDIRECT mode
*/
void iinchip_init(void)
{
    setMR(MR_RST);
    #ifdef __DEF_IINCHIP_DBG__
    Ashell_print("MR value is %02x \r\n",getMR());
    #endif
}

/**
@brief  This function set the transmit & receive buffer size as per the channels is used
Note for TMSR and RMSR bits are as follows\n
bit 1-0 : memory size of channel #0 \n
bit 3-2 : memory size of channel #1 \n
bit 5-4 : memory size of channel #2 \n
bit 7-6 : memory size of channel #3 \n
bit 9-8 : memory size of channel #4 \n
bit 11-10 : memory size of channel #5 \n
bit 12-12 : memory size of channel #6 \n
bit 15-14 : memory size of channel #7 \n
Maximum memory size for Tx, Rx in the W5500 is 16K Bytes,\n
In the range of 16KBytes, the memory size could be allocated dynamically by each channel.\n
Be attentive to sum of memory size shouldn't exceed 8Kbytes\n
and to data transmission and receiption from non-allocated channel may cause some problems.\n
If the 16KBytes memory is already  assigned to centain channel, \n
other 3 channels couldn't be used, for there's no available memory.\n
If two 4KBytes memory are assigned to two each channels, \n
other 2 channels couldn't be used, for there's no available memory.\n
*/
void w5500sysinit( uint8_t * tx_size, uint8_t * rx_size  )
{
	int16_t i;
	int16_t ssum,rsum;
	#ifdef __DEF_IINCHIP_DBG__
	Ashell_print("sysinit()\r\n");
	#endif
	ssum = 0;
	rsum = 0;

	for (i = 0 ; i < _WIZCHIP_SOCK_NUM_; i++)       // Set the size, masking and base address of Tx & Rx memory by each channel
	{
		//WIZCHIP_WRITE( (Sn_TXBUF_SIZE(i)), tx_size[i]);
		//WIZCHIP_WRITE( (Sn_RXBUF_SIZE(i)), rx_size[i]);
		setSn_TXBUF_SIZE(i, tx_size[i]); 
		setSn_RXBUF_SIZE(i, rx_size[i]); 
		
		#ifdef __DEF_IINCHIP_DBG__
			Ashell_print("tx_size[%d]: %d, Sn_TXMEM_SIZE = %d\r\n",i, tx_size[i], getSn_RXBUF_SIZE(i));
			Ashell_print("rx_size[%d]: %d, Sn_RXMEM_SIZE = %d\r\n",i, rx_size[i], getSn_TXBUF_SIZE(i));
		#endif 
		
		SSIZE[i] = (int16_t)(0);
		
		RSIZE[i] = (int16_t)(0);

		if (ssum <= 16384)
		{
			switch( tx_size[i] )
			{
				case 1:
					SSIZE[i] = (int16_t)(1024);
				break;
				case 2:
					SSIZE[i] = (int16_t)(2048);
				break;
				case 4:
					SSIZE[i] = (int16_t)(4096);
				break;
				case 8:
					SSIZE[i] = (int16_t)(8192);
				break;
				case 16:
					SSIZE[i] = (int16_t)(16384);
				break;
				default :
					RSIZE[i] = (int16_t)(2048);
				break;
			}
		}
		if (rsum <= 16384)
		{
			switch( rx_size[i] )
			{
				case 1:
					RSIZE[i] = (int16_t)(1024);
				break;
				case 2:
					RSIZE[i] = (int16_t)(2048);
				break;
				case 4:
					RSIZE[i] = (int16_t)(4096);
				break;
				case 8:
					RSIZE[i] = (int16_t)(8192);
				break;
				case 16:
					RSIZE[i] = (int16_t)(16384);
				break;
				default :
					RSIZE[i] = (int16_t)(2048);
				break;
			}
		}
		ssum += SSIZE[i];
		rsum += RSIZE[i];
	}
}
 

/******************* (C) COPYRIGHT 2018 CIQTEK Samuel *****END OF FILE****/
